package cz.vse.adventura.po1715.xforv00_forejt.gui._04_abstract;

import cz.pecinovsky.english.pojj.adv_framework.game_gui.IGameG;
import cz.pecinovsky.english.pojj.adv_framework.game_gui.IUIG;
import cz.pecinovsky.english.pojj.adv_framework.game_txt.IGame;
import cz.pecinovsky.english.pojj.adv_framework.gui_stage.IGUI_04;
import cz.pecinovsky.english.pojj.adv_framework.test_util._Test_101;
import cz.vse.adventura.po1715.xforv00_forejt.hra.Hra;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import java.awt.event.ActionEvent;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JMenuBar;
import javax.swing.JOptionPane;


/*******************************************************************************
 * Instance třídy {@code GUI_O4} představují
 * grafické uživatelsé rozhraní umožňující hrát hry
 * vyhovující podmínkám rámce (frameworku) definovaného v balíčku
 * {@code cz.pecinovsky.english.pojj.adv_framework.game_txt},
 * tj. textové hry vyhovující požadavkům minulého semestru.
 *
 * @author    Vladimír FOREJT
 * @version   0.00.000
 */
public class GUI_04 implements IUIG, IGUI_04
{
    /** Pro účely debuggingu - nejdříve breakpoint při zavádění
     *      třídy, pak při vytváření instance.
     */
    /*static{
        System.out.println("Zavádí se třída " + GUI_04.class);
    }
    {
        System.out.println("Vytváří se instance třídy " + this.getClass());
    } */
//== KONSTANTNÍ ATRIBUTY TŘÍDY =================================================
    
    /** Souřadnice, na které se aplikační okno přesune po spuštění GUI_O4.
     *  To má smysl především při zobrazování na sekundárním monitoru. */
    //private static final Point POINT_0 = new Point(0, 0);   //-300, 0);

    private static final int OUTPUT_WIDTH = 500;
    private static final int  OUTPUT_HEIGHT = 400;
    private static final Dimension PREFERRED_OUTPUT_SIZE = new Dimension(OUTPUT_WIDTH, OUTPUT_HEIGHT);
    
   
    /** Odstup mezi jednotlivými prvky ve svisle uspořádaném panelu. */
    private static final int VERTICAL_DISTANCE = 10;
    private static final int HORIZONTAL_DISTANCE = 10;

    /** Autor a XNAME */
    private static final String author = "FOREJT Vladimír";
    private static final String xname = "XFORV00";

//== PROMĚNNÉ ATRIBUTY TŘÍDY ===================================================
//== STATICKÝ INICIALIZAČNÍ BLOK - STATICKÝ KONSTRUKTOR ========================
//== KONSTANTNÍ ATRIBUTY INSTANCÍ ==============================================

    /** Hlavní okno GUI_O4 */
    private final JFrame appWindow = new JFrame("První okno");

    /** Posluchač vstupního pole, který reaguje na uživatelovy příkazy
    *    a na příkazy zadané přes metodu {@link #executeCommand(String) }*/
    private final ActionListener inputListener = new TxfListener();
    
    /** Vstupní pole, kam uživatel zadává příkazy */
    private final InputPane inputPane = new InputPane(this, inputListener);


    /** Výstupní okno, kde hra vypisuje odpověď na uživatelovy příkazy */
    private final OutputPane outputPane = new OutputPane(this, PREFERRED_OUTPUT_SIZE);

    

    /** Panel zborazující sousedy prostoru, v němž se hráč nachází. */
    private final NeighborPane neighborPane = new NeighborPane(this, VERTICAL_DISTANCE);

    /** Panel zborazující předměty v prostoru, v němž se hráč nachází. */
    private final ObjectPane objectPane = new ObjectPane(this, VERTICAL_DISTANCE);

    /** Panel zborazující předměty v batohu */
    private final BagPane bagPane = new BagPane(this, HORIZONTAL_DISTANCE);
    
    private final Box southPane = new Box(BoxLayout.Y_AXIS);

    /** Okno s plánem hry */
    private final GameMap mapWindow = new GameMap(this);
    
    /** Záznam zadávaných příkazů */
    private final ActivityLog log = new ActivityLog(this);
    
//== PROMĚNNÉ ATRIBUTY INSTANCÍ ================================================

    /** Hra, která se bude hrát pomocí tohoto GUI_O4 */
    private IGameG thisGame;
    
    /** Stav zobrazení plánku hry */
    private Boolean mapState = true;

    /** Panel všech příkazů hry */
    private CommandsPane cmdPane;
   
//== PŘÍSTUPOVÉ METODY VLASTNOSTÍ TŘÍDY ========================================
//== OSTATNÍ NESOUKROMÉ METODY TŘÍDY ===========================================

//##############################################################################
//== KONSTRUKTORY A TOVÁRNÍ METODY =============================================

    /***************************************************************************
     *Vytvoří instanci GUI_O4, které bude komunikovat s hrou
     * zadanou v parametru
     *
     * Co se týká celého okna, zůstává v konstruktoru, co se týká částí,
     *      přesouvám do příslušných metod, které se o ně starají
     * @param game Hra, která se použije
     */
    public GUI_04()
    {
        prepareMenu();
        
        appWindow.add(southPane,    BorderLayout.SOUTH );
        southPane.add(inputPane);
        
        appWindow.add(outputPane,   BorderLayout.CENTER);
        appWindow.add(neighborPane, BorderLayout.WEST  );
        appWindow.add(objectPane,   BorderLayout.EAST  );
        appWindow.add(bagPane,      BorderLayout.NORTH );

        /** Co se stane, když uživatel zavře okno
         * EXIT_ON_CLOSE ukončí aplikaci */
        appWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        
       
        
    }





//== ABSTRAKTNÍ METODY =========================================================
//== PŘÍSTUPOVÉ METODY VLASTNOSTÍ INSTANCÍ =====================================

    /***************************************************************************
     * Vrátí jméno autora scénáře ve formátu <b>PŘÍJMENÍ Křestní</b>,
     * tj. neprve příjmení psané velkými písmeny a za ním křestní jméno,
     * u nějž bude velké pouze první písmeno a ostatní písmena budou malá.
     *
     * @return Jméno autora/autorky programu ve tvaru PŘÍJMENÍ Křestní
     */
    @Override
    public String getAuthor()
    {
        return author;
    }


    /***************************************************************************
     * Vrátí x-name autora/autorky programu zapsané velkými písmeny.<p>
     * X-name má tvar <b>{@code XPPPK99}</b> nebo <b>{@code XPPPK999}</b>,
     * kde<br>
     * <ul>
     *   <li><b>{@code PPP}</b> zastupuje první tří písmena příjmení autora(ky)
     *      zapsaná velkými písmeny, </li>
     *   <li><b>{@code K}</b> zastupuje první písmeno křestního jména a </li>
     *   <li><b>{@code 99}</b>, resp. <b>{@code 999}</b> zastupují číslice
     *      přiřazené systémem, pomocí nichž se rozsliší studenti(ky)
     *      s předchozími znaky shodnými.
     * </ul>
     *
     * @return X-name autora/autorky programu
     */
    @Override
    public String getXNAME()
    {
        return xname;
    }

    /***************************************************************************
     * Vrací odkaz na aktuálně hranou hru
     *
     * @return Odkaz na naposledy/právě hranou hru
     */
     @Override
     public IGameG getGame() {
         return thisGame;
     }

     /***************************************************************************
     * Vrátí naposledy zadaný příkaz.
     *
     * @return Text napsoledy zadnaého příkazu
     */
    @Override
    public String getCommand() {
        String command = inputPane.getText();
        return command;
    }
    
    /***************************************************************************
     * Vrací oblast aplikačního okna
     *
     * @return Oblast
     */
    Rectangle getBounds() {
        return appWindow.getBounds();
    }
    
    /***************************************************************************
     * Stav, jestli je plánek místností zobrazován nebo ne
     * 
     * @Return stav zobrazování mapy
     */
    Boolean isMapVisible() {
        return mapState;
    }
    
    /***************************************************************************
     * Zapne nebo vypne zobrazení plánku místností
     *
     * @param visible - stav zobrazení true/false
     */
    void setMapVisible (Boolean visible) {
        mapState = visible;
        mapWindow.setVisible(mapState);
    }
    
    /***************************************************************************
     * Vrátí odkaz na log, ve kterém gui zaznamenává průběh hry
     */
    ActivityLog getLog() {
        return log;
    }
   
    /***************************************************************************
     * Vrací odkaz na okno aplikace - můžou použít další komponenty
     */
    JFrame getAppWindow() {
        return appWindow;
    }
    
    /***************************************************************************
     * Nastaví text v input panelu
     */
    void setInputText(String text) {
        inputPane.setText(text);
        inputPane.requestFocus();
    }
    
//== OSTATNÍ NESOUKROMÉ METODY INSTANCÍ ========================================

     /***************************************************************************
     * Spustí komunikaci mezi zadanou hrou a danou instancí GUI_O4
     * mající na starosti komunikaci s uživatelem.
     * <p>
     * Metoda před pokusem o spuštění zadané hry nejprve zkontroluje,
     * že pomocí daného GUI_O4 není akutálně hrána žádná hra
     * a že ani zadaná hra není rozehrána. V opačném případě vyhodí výjimku.
     * <p>
     * Tato verze ještě není plnohodntou implementací,
     * protože je schopna obsluhovat i hry implementující rozhraní
     * a proto není pravda, že: "<i>nejprve však zkontroluje,
     * že zadaná hra je ve skutečnosti instancí rozhraní {@link IGameG},
     * protože jiné neposkytují metody, které grafické UI očekává</i>".
     *
     * @param game Hra, kterou ma dané UI spustit
     * @throws IllegalArgumentException
     *         Existují důvody, které brání spuštění zadané hry
     */
    @Override
    public void startGame(IGame game)
    {
        if ((game == null) || (! game.isReady())) {
            throw new RuntimeException("Zadaná hra je prázdná nebo ještě běží");
        }
        /** Kontrola, jestli není právě rozehraná hra */
        if ((thisGame != null) && (! thisGame.isReady())) {
            int answer = JOptionPane.showConfirmDialog(appWindow,
                    "V současné době je rozehrána hra\n\n" + thisGame.getClass() +
                    "Opravdu chcete tuto hru ukončit a začít hrát hru\n\n " +
                    game.getClass());
            /** Když stiskne YES, pokračuje (ukončí hranou hru) */
            if (answer != 0) {
                return;
            }
        }
        thisGame = (IGameG) game;
        appWindow.setTitle(thisGame.getAuthor());
        /** GUI_O4 může mít rozmístěné komponenty ze staré hry
         * -> inicializovat pro zadanou hru*/
        
        initialize();

        executeCommand("");     //Odstartuje zadáním prázdného příkazu


    }


    /***************************************************************************
     * Spustí komunikaci mezi implicitní hrou a danou instancí
     * mající na starosti komunikaci s uživatelem.
     */
    @Override
    public void startGame()
    {
        IGameG choosedGame = Hra.getHra();
        startGame(choosedGame);
    }


    /***************************************************************************
     * Zpracuje příkaz předaný v parametru jako by byl zadán standardním
     * postupem z klávesnice. Metoda umožňuje testovat reakce daného
     * uživatelského rozhraní na příkazy zadávané z klávesnice.
     *
     * @param command Zadávaný příkaz
     */
    @Override
    public void executeCommand(String command)
    {
        //Zadá obdržený příkaz do vstupního pole jako zadaný text
        inputPane.setText(command);

        //Zavolá metodu posluchače čekajícího na zadaní příkazu hry
        //Tato metoda zpracuje příkaz jako kdyby jej zadal hráč
        inputListener.actionPerformed(null);
    }
    
    
    
    
    

//== SOUKROMÉ A POMOCNÉ METODY TŘÍDY ===========================================
//== SOUKROMÉ A POMOCNÉ METODY INSTANCÍ ========================================
    /***************************************************************************
     * Inicializuje GUI (aplikační okno) tak, aby se mohla začít hrát nová hra.
     */
    private void initialize() {
        /**Nejdřív jednotlié oblasti */
        inputPane.initialize();
        outputPane.initialize();
        neighborPane.initialize();
        bagPane.initialize();
        objectPane.initialize();

        if(cmdPane != null) {
            southPane.remove(cmdPane);
        } 
        cmdPane = new CommandsPane(thisGame, this);
        southPane.add(cmdPane);
        
        /** Nastaví oknu nejmenší možnou velikost
         *(v závislosti na jeho komponentách */
        appWindow.pack();

        //Zviditelní okno
        appWindow.setVisible(true);
        
        /** Plánek místností */
        mapWindow.initialize();
    }
    


    /***************************************************************************
     * Připraví hlavní nabídku (menu bar).
     */
    private void prepareMenu()
    {
        JMenuBar menuBar = new JMenuBar();
        appWindow.setJMenuBar(menuBar);

        MenuGame menuGame = new MenuGame(this);
        MenuRecord  menuRecord = new MenuRecord(this);
        MenuHelp  menuHelp = new MenuHelp(this);

        menuBar.add(menuGame);
        menuBar.add(menuRecord);
        menuBar.add(menuHelp);
    }

    

//== INTERNÍ DATOVÉ TYPY =======================================================
    /***************************************************************************
     * Vnitřní třída, jejíž instance představují posluchače
     * vstupního textového pole čekající na stisk klávesy {@code Enter}
     * v okamžiku, kdy má pole fokus.
     */
    private class TxfListener implements ActionListener
    {
        /***********************************************************************
         * Zjistí aktuální text ve vstupním poli (= text zadaného příkazu),
         * zapíše jej do výstupní oblasti,
         * předá zadaný příkaz obsluhované hře ke zpracování
         * a do výstupního pole zapíše obdržený výsledek.
         *
         * @param e Objekt, který je schopen poskytnout informace o události,
         *          avšak v této verzi není využíván
         */
        @Override
        public void actionPerformed(ActionEvent e)
        {
            //Začneme aktualizací vystupní oblasti, protože zadává příkaz hře
            outputPane.update();

            neighborPane.update();
            objectPane.update();
            bagPane.update();
            
            //Záznam do logu - nejdřív kontrola, jestli se má vůbec záznam dělat
            if(log.getRecordInFile() || log.isRecorded()) {
                log.recordCommand(getCommand());
            }
            
            //Skončíme aktualizací vstupní oblasti,
            //protože se při ní definitivně zapomíná zadaný příkaz
            inputPane.update();
            
            appWindow.repaint();
            appWindow.validate();
        }


    }
//== TESTY A METODA MAIN =======================================================
//
//    /***************************************************************************
//     * Testovací metoda.
//     */
//    public static void test()
//    {
//        GUI_O4 inst = new GUI_O4();
//    }
//    /** @param args Parametry příkazového řádku - nepoužívané. */
//    public static void main( String[] args )  {  test();  }
    /***************************************************************************
     * Testovací metoda předvede hraní implicitní hry
     * prostřednictvím gui - instance této třídy.
     */
    public static void test()
    {
        /**GUI_O4   gui   = new GUI_O4(null);
        IGame    gameX = _Test_101.getDefaultGame();
        _Test_101.playGameByScenario(gameX, 2, gui);*/
         IGame  gameX = Hra.getHra();
        GUI_04 gui   = new GUI_04();
        _Test_101.playGameByScenario(gameX, 2, gui);
    }
    /** @param args Parametry příkazového řádku - nepoužívané. */
//    public static void main( String[] args )  {  test();  }

//    {
//        System.out.println("Vytvořila se instance třídy " + this.getClass());
//    }
//    static{
//        System.out.println("Zavedla se třída " + GUI_04.class);
//    }
    
}
